﻿#include "ais_vector.h"
#include "kernel/transform.h"
#include "math/nonlinear_math.h"

CAGD::AIS_Vector::AIS_Vector(Handle(GeomVector) vec, Handle(GeomPoint) origin) : m_vector(vec), m_origin(origin)
{
    m_info.size = 1;
}

bool CAGD::AIS_Vector::MoveTo(Point pos)
{
    // 隐藏状态不交互，不活跃状态不交互
    if (m_info.status == AIS_Status::HIDDEN || m_info.active == false)
        return false;

    bool hang = false;
    m_info.status = AIS_Status::NONE;

    // 取消前 0.1 部分的选中，配合向量着色器的显示
    for (int i = 10; i < 90; i++)
    {
        BPnt p1 = m_origin->Point() + m_vector->Vector() * (i - 1) / 99.0;
        BPnt p2 = m_origin->Point() + m_vector->Vector() * i / 99.0;

        float d1, d2;
        BPnt2d q1 = m_context.GetScreenPoint(Transform::Perform(m_info.modelMat, BPntf(p1)), d1);
        BPnt2d q2 = m_context.GetScreenPoint(Transform::Perform(m_info.modelMat, BPntf(p2)), d2);

        // 跳过不在屏幕内的点
        if (d1 > 1 || d2 > 1)
            continue;

        // 确保共线
        BPnt2d q0 = {float(pos.x), float(pos.y)};
        float d01 = q0.Distance(q1);
        float d02 = q0.Distance(q2);
        float d12 = q1.Distance(q2);

        if (fabs(d01 + d02 - d12) < 5)
        {
            // 始终是 HANG，确保向量只要被选中就一直被选中
            hang = true;
            m_info.status = AIS_Status::HANG;

            // 计算深度
            SetDepth((d1 * d02 + d2 * d01) / (d01 + d02));
            break;
        }
    }
    return hang;
}

bool CAGD::AIS_Vector::Move(Point pos)
{
    auto &info = m_context.GetInfo();

    // 获得必要点
    BPnt stickPos = Transform::Perform(m_info.modelMat, BPntf(m_stick));
    BPnt stickVec = Transform::Perform(m_info.modelMat, BVecf(m_vector->Vector()));
    BPnt viewPos = info.viewPos;

    // 通过 stickPos2d, testPos2d, targetPos2d 计算处目标位置
    float d;
    BPnt2d pos2d(pos.x, pos.y);
    BPnt2d stickPos2d = m_context.GetScreenPoint(stickPos, d);
    BPnt2d testPos2d = m_context.GetScreenPoint(stickPos + stickVec, d);
    BPnt2d targetPos2d = stickPos2d + (testPos2d - stickPos2d).Normalized() *
                                          ((pos2d - stickPos2d) * (testPos2d - stickPos2d).Normalized());
    auto targetPos = m_context.GetSpacePoint({int(targetPos2d.X()), int(targetPos2d.Y())});

    FuncXX f = [=](Vector xy) -> Vector {
        Vector r(3);
        BPnt p = viewPos + (targetPos - viewPos) * xy[0] - stickPos - stickVec * xy[1];
        r[0] = p.X();
        r[1] = p.Y();
        r[2] = p.Z();
        return r;
    };

    // 构造方程
    NewtonSolver solver;
    Vector xy(2);
    solver(f, xy);

    // 修改起点位置
    m_origin->SetPoint(Transform::Perform(Transform::Inverted(m_info.modelMat), BPntf(stickPos + stickVec * xy[1])));

    // 刷新着色器
    UpdateArray();

    // 发送移动消息
    PassMessageUp(Message::MOVE_CHILD);

    return true;
}

bool CAGD::AIS_Vector::Click(Point pos)
{
    if (m_info.active)
    {
        // 点击时记录位置
        bool hang = AIS_Base::Click(pos);
        m_stick = m_origin->Point();
        return hang;
    }
    return false;
}

bool CAGD::AIS_Vector::Release(Point pos)
{
    // 如果没选中就恢复正常，如果选中，MoveTo 已经设为 HANG，不用修改
    if (!MoveTo(pos))
        m_info.status = AIS_Status::NONE;

    // 一定释放
    return true;
}

CAGD::AIS_Type CAGD::AIS_Vector::GetType() const
{
    return AIS_Type::VECTOR;
}

void CAGD::AIS_Vector::UpdateInfo()
{
    // 继承模型矩阵
    auto parent = GetParent();
    if (parent != nullptr)
    {
        parent->UpdateInfo();
        m_info.modelMat = parent->GetInfo().modelMat;
    }

    // 颜色与向量方向有关
    RGB color = m_vector->Vector().Normalized().Absoluted();
    if (m_info.status == AIS_Status::NONE)
        m_info.color = {color.X(), color.Y(), color.Z(), 1.0f};
    else
        m_info.color = {0, 0, 0, 1};
}

void CAGD::AIS_Vector::RecvMessage(Message mess)
{
    // 先调用默认接收函数
    AIS_Base::RecvMessage(mess);

    switch (mess)
    {
    case CAGD::Message::SELECT: {
        PassMessageUp(Message::SHOW);
        break;
    }
    case CAGD::Message::SHOW:
    case CAGD::Message::SCALE: {
        if (m_info.status != AIS_Status::HIDDEN)
        {
            BPnt view = m_context.GetInfo().viewPos;
            BPnt pos = Transform::Perform(m_info.modelMat, BPntf(m_origin->Point()));

            // 操作轴长度根据镜头到点的距离变换
            m_vector->SetVector(m_vector->Vector().Normalized() * view.Distance(pos) / 10);
            UpdateArray();
        }
        break;
    }
    default:
        break;
    }
}

Handle(CAGD::GeomVector) CAGD::AIS_Vector::GetVector() const
{
    return m_vector;
}

Handle(CAGD::GeomPoint) CAGD::AIS_Vector::GetOrigin() const
{
    return m_origin;
}
